use base_data::{channel::Channel, outside::switch::OutSideCommand};
use futures::future::BoxFuture;
use std::sync::OnceLock;
use tokio::sync::{broadcast, mpsc};
use tools::global_defin::tokio_runtime::get_rt;
use tracing::{error, info};

use crate::{
	empty_sockaddr,
	sip2::{
		config::sip_config,
		register::manager::start_sip_registe,
		transport::{
			gw::{EXTERNAL_GW, ISP_GW},
			udp::server::listener_udp,
			TransportProtocol,
		},
	},
};

use super::{dialog::DialogEvent, distribute::start_distribute, register::SipRegEvent};

///开始监听..所监听的地址是由配置文件指定的.
///
/// 目前监听两个地址.一个ips 一个外部网关,此两个地址可以相等
/// 此方法仅第一次调用有效,后续的调用是无效果
/// 同时需要给出监听时创建的回调函数,
pub fn listen_device<F, R, FU>(
	handle_create: &'static F,
	handle_register: &'static R,
	get_user_passwd: FU,
) where
	F: Fn(Channel) + Send + Sync,
	R: Fn(SipRegEvent) -> () + Send + Sync,
	FU: Fn(&str) -> BoxFuture<Option<String>> + 'static + Send + Sync,
{
	let listen_isp_addr = sip_config().listen_isp_addr;
	info!("sip 开始监听:{}", listen_isp_addr);

	//目的是控制单次启动.
	TO_DISTRIBUTE.get_or_init(|| {
		let send_to_reg_control = start_sip_registe(handle_register, get_user_passwd);

		let sen = start_distribute(handle_create, send_to_reg_control);
		let sender = sen.clone();
		//向所有传输层监听发送消息的发送者
		let to_lis = get_to_all_listener();
		get_rt().spawn(async move {
			let Ok(sock) = listener_udp(listen_isp_addr, sender.clone(), to_lis.subscribe()).await else {
				error!("监听失败:{}", listen_isp_addr);
				return;
			};

			let _ = ISP_GW.get_or_init(|| TransportProtocol::Udp(sock.clone(), sip_config().isp_addr));
			let listen_external_addr = sip_config().listen_external_addr;
			if listen_external_addr == listen_isp_addr {
				info!("监听地址和外部地址相同,不进行外部地址监听");
			} else {
				let Ok(sock) = listener_udp(listen_external_addr, sender.clone(), to_lis.subscribe()).await
				else {
					error!("监听失败:{}", listen_isp_addr);
					return;
				};

				let _ = EXTERNAL_GW.get_or_init(|| TransportProtocol::Udp(sock.clone(), empty_sockaddr()));
			}
		});

		sen
	});
}

///向分发模块发送消息的发布者.
static TO_DISTRIBUTE: OnceLock<mpsc::Sender<DialogEvent>> = OnceLock::new();
pub(crate) fn to_distribute() -> &'static mpsc::Sender<DialogEvent> {
	TO_DISTRIBUTE.get().unwrap()
}

pub(crate) fn get_to_all_listener() -> &'static broadcast::Sender<OutSideCommand> {
	static TO_ALL_SIP_LISTENER: OnceLock<broadcast::Sender<OutSideCommand>> = OnceLock::new();

	let to_lis = TO_ALL_SIP_LISTENER.get_or_init(|| {
		let (s, _r) = broadcast::channel(100);

		s
	});

	to_lis
}
